﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading;
using Kugar.Core.Communications.Transfer;
using Kugar.Core.BaseStruct;
using Kugar.Core.MultiThread;

namespace Kugar.Core.Communications.MasterSlaveCommunication
{
    /// <summary>
    ///     主从应答式通信的主机端,,启用发送队列,并且只有在客户机回复数据或者发送超时时才会发送下一个数据包 <br/>
    ///     常用于Modbus等协议
    /// </summary>
    public interface IMasterSide :IDisposable
    {
        /// <summary>
        ///     将待发送的数据压入发送队列中,等待发送
        /// </summary>
        /// <param name="buffer">发送的数据区</param>
        /// <param name="offset">偏移量</param>
        /// <param name="count">发送总数量</param>
        /// <param name="callback">发送后的回调函数</param>
        /// <param name="state">用户定义的状态</param>
        /// <returns></returns>
        IAsyncResult BeginSend(ISendQueueItem sendItem, AsyncCallback callback, object state);

        int EndSend(IAsyncResult iar);

        /// <summary>
        ///     相隔两次的发送间隔时间，单位为毫秒
        /// </summary>
        int SendDelay { set; get; }

        ITransfer Transfer { get; }

        ISendQueue SendQueue { get; }

        int ReceiveBuffer { set; get; }

        MasterSideReceiveDataChecker ReceiveDataChecker { get; }

        event EventHandler<DataReceiveEventArgs> ReceiveDataCompleted;

        //event EventHandler<MasterSendEventArgs> BeforceSendData;

        //event EventHandler<MasterSendEventArgs> AfterSendData;

        //event EventHandler<MasterReceiveEventArgs> AfterReceiveData;

        
    }

    public delegate bool MasterSideReceiveDataChecker(ISendQueueItem sendedItem,ArraySegment<byte> receiveData);

    public interface ISendQueue:IDisposable
    {
        ISendQueue Dequeue();

        ISendQueueItem BuildSendItem(byte[] buffer, int offset, int count);

        bool TryDequeue(out ISendQueueItem item);

        bool Enqueue(ISendQueueItem item);

        void Clear();

        void Close();
    }

    public interface ISendQueueItem:IDisposable
    {
        byte[] Buffer { get; }
        int Count { get; }
        int Offset { get; }
        DateTime? SendTime { set; get; }
        int TimeoutCount { get; set; }
        DateTime? EnqueueTime { set; get; }

        void OnBeforceSend();
        void OnAfterSend();

        void OnAfterReceive(ArraySegment<byte> e);
    }



    public class MasterSide:MarshalByRefObject,IMasterSide
    {
        protected ISendQueue _sendQueue;
        protected ITransfer _transfer;
        private TimerEx _timeoutCheckerTimer = null;  //发送定时器
        protected MasterSideReceiveDataChecker _receiveChecker = null;
        protected ISendQueueItem _sendingItem = null;
        protected static CallbackTimer _callbackTimer = new CallbackTimer(5, 5);
        protected int _sendDelay = 500;
        protected Dictionary<ISendQueueItem, MasterSideSendAsyncResult> _cacheCallback = null;
        protected int _receiveBuffer = 1024;
        private byte[] _buffer=new byte[1024];

        public MasterSide(ITransfer transfer,ISendQueue sendQueue)
        {
            _timeoutCheckerTimer = new TimerEx(SendTimerCallback,200,null);

            _transfer = transfer;
            _sendQueue = sendQueue;

            _transfer.AfterReceiveData += Transfer_AfterReceiveData;

            _timeoutCheckerTimer.IsStopWhenRun=true;
            _timeoutCheckerTimer.Start();

            
        }

        void Transfer_AfterReceiveData(object sender, DataReceiveEventArgs e)
        {
            if (e.HasError || !OnReceiveChecker(_sendingItem,e.ReceiveData))
            {
                //TODO:引发错误处理事件
               return; 
            }


            ISendQueueItem tempItem = null;

            if(_sendQueue.TryDequeue(out tempItem))
            {
                Interlocked.Exchange(ref _sendingItem, tempItem);

                _callbackTimer.Call(_sendDelay, OnSendDataInternal, tempItem);
            }

            OnReceiveDataCompleted(e.ReceiveData);

        }

        private void SendTimerCallback(object state)
        {
            
        }

        private void OnSendDataInternal(object state)
        {
            var t = (ISendQueueItem) state;

            if (t==null)
            {
                return;
            }

            InterlockedEx.IfThen(ref _sendingItem, t, (v, s) =>
                                                          {
                                                              v.OnBeforceSend();
                                                              _transfer.BeginSend(v.Buffer, v.Offset, v.Count,OnSendAsyncCallbackInternal, v)
                                                          }, null);

        }

        private void OnSendAsyncCallbackInternal(IAsyncResult iar)
        {
            Exception error = null;

            var t = _transfer.EndSend(iar,ref error);

            var s = (ISendQueueItem)iar.AsyncState;

            s.OnAfterSend();

            MasterSideSendAsyncResult ar = null;

            if (_cacheCallback.TryGetValue(s, out ar))
            {
                ar.Execute(t, error);
            }
        }

        private void OnReceiveDataCompleted(ArraySegment<byte> data)
        {
            if (ReceiveDataCompleted!=null)
            {
                Events.EventHelper.Raise(ReceiveDataCompleted,this,new DataReceiveEventArgs(data,_transfer));
            }
        }

        private void OnBeginSend()
        {
            if (_buffer.Length <_receiveBuffer)
            {
                _buffer=new byte[_receiveBuffer];
            }


        }
        
        #region Implementation of IMasterSide

        /// <summary>
        ///     将待发送的数据压入发送队列中,等待发送
        /// </summary>
        /// <param name="buffer">发送的数据区</param>
        /// <param name="offset">偏移量</param>
        /// <param name="count">发送总数量</param>
        /// <param name="callback">发送后的回调函数</param>
        /// <param name="state">用户定义的状态</param>
        /// <returns></returns>
        public virtual IAsyncResult BeginSend(ISendQueueItem sendItem, AsyncCallback callback, object state)
        {
            //var item = _sendQueue.BuildSendItem(buffer, offset, count);

            var iar = new MasterSideSendAsyncResult(sendItem, callback, state);

            _cacheCallback.Add(sendItem, iar);

            _sendQueue.Enqueue(sendItem);

            return iar;
        }

        public virtual int EndSend(IAsyncResult iar)
        {
            var t = iar as MasterSideSendAsyncResult;

            if (t==null)
            {
                throw new ArgumentNullException("iar");
            }

            return t.SendedCount;
        }

        /// <summary>
        ///     相隔两次的发送间隔时间，单位为毫秒
        /// </summary>
        public virtual int SendDelay
        {
            get { return _sendDelay; }
            set { _sendDelay=value; }
        }

        public virtual ITransfer Transfer
        {
            get { return _transfer; }
        }

        public virtual ISendQueue SendQueue
        {
            get { return _sendQueue; }
        }

        public virtual int ReceiveBuffer
        {
            get { return _receiveBuffer; }
            set
            {
                Interlocked.Exchange(ref _receiveBuffer, value);
            }
        }

        public virtual MasterSideReceiveDataChecker ReceiveDataChecker
        {
            get { throw new NotImplementedException(); }
        }

        public event EventHandler<DataReceiveEventArgs> ReceiveDataCompleted;

        #endregion

        #region Implementation of IDisposable

        public void Dispose()
        {
            throw new NotImplementedException();
        }

        #endregion


        private bool OnReceiveChecker(ISendQueueItem sendedItem, ArraySegment<byte> receiveData)
        {
            if (_receiveChecker!=null)
            {
                return _receiveChecker(sendedItem, receiveData);
            }
            else
            {
                return true;
            }
        }

        public class MasterSideSendAsyncResult:AsyncResultBase
        {
            private ISendQueueItem _sendedItem=null;
            private int _sendedCount=-1;

            public MasterSideSendAsyncResult(ISendQueueItem sendedItem, AsyncCallback callback, object state)
                : base(callback, state)
            {
                _sendedItem = sendedItem;
            }

            public ISendQueueItem SendedItem { get { return _sendedItem; } }

            public int SendedCount { get { return _sendedCount; } }

            public void Execute(int count, Exception error)
            {
                if (error == null)
                {
                    _sendedCount = count;
                }

                base.Execute(error);

            }
        }
    }

    public abstract class MasterSideBase : MarshalByRefObject,IMasterSide,IDisposable
    {
        protected int sendDelayTime=0;
        protected DataStructToSendBase dataSended = null;
        protected bool isDisposed = false;



        public abstract void SendAsyn(DataStructToSendBase sendStruct);

        public bool IsOpen
        {
            get
            {
                return Transfer.IsActivity;
            }
        }

        public virtual int SendDelayTime
        {
            get { return sendDelayTime; }
            set
            {
                if (value<100)
                {
                    value = 100;
                }

                Interlocked.Exchange(ref sendDelayTime, value);
            }
        }

        public TransferBase Transfer { private set; get; }

        public abstract DataStructToSendBase[] RemoveSendingStruct(Predicate<DataStructToSendBase> checker);

        protected abstract bool CheckReceivedData(DataStructToSendBase sendedStruct,  byte[] data);

        protected void OnBeforceSendData()
        {
            OnBeforceSendData(dataSended, null);
        }

        protected void OnBeforceSendData(Exception error)
        {
            OnBeforceSendData(dataSended, error);
        }

        protected void OnBeforceSendData(DataStructToSendBase sended, Exception error)
        {
            DataStructToSendBase tempStruct = null;

            Interlocked.Exchange(ref tempStruct, sended);

            ThreadPool.QueueUserWorkItem((s) =>
                                             {
                                                 if (BeforceSendData != null)
                                                 {
                                                     var lst = BeforceSendData.GetInvocationList();

                                                     foreach (EventHandler<MasterSendEventArgs> del in lst)
                                                     {
                                                         try
                                                         {
                                                             del(this, new MasterSendEventArgs { Error = error, SendedSData = sended });
                                                         }
                                                         catch (Exception)
                                                         {
                                                             continue;
                                                         }
                                                     }

                                                     //BeforceSendData(this,new MasterSendEventArgs {Error = error,SendedSData = sended});
                                                 }

                                                 sended.OnBeforceSendData(this);
                                             });
        }

        protected void OnAfterSendData()
        {
            OnAfterSendData(dataSended, null);
        }

        protected void OnAfterSendData(Exception error)
        {
            OnAfterSendData(dataSended, error);
        }

        protected void OnAfterSendData(DataStructToSendBase sended, Exception error)
        {
            DataStructToSendBase tempStruct = null;

            Interlocked.Exchange(ref tempStruct, sended);

            ThreadPool.QueueUserWorkItem((s) =>
                                             {
                                                 if (AfterSendData != null)
                                                 {
                                                     var lst = AfterSendData.GetInvocationList();

                                                     foreach (EventHandler<MasterSendEventArgs> del in lst)
                                                     {
                                                         try
                                                         {
                                                             del(this,
                                                                 new MasterSendEventArgs
                                                                     {Error = error, SendedSData = sended});
                                                         }
                                                         catch (Exception)
                                                         {
                                                             continue;
                                                         }
                                                     }
                                                 }

                                                 sended.OnAfterSendData(this,error);
                                             });





            //AfterSendData(this, new MasterSendEventArgs { Error = error, SendedSData = sended });
        
        }
        

        protected void OnAfterReceiveData(byte[] data)
        {
            OnAfterReceiveData(dataSended,data, null);
        }

        protected void OnAfterReceiveData(byte[] data, Exception error)
        {
            OnAfterReceiveData(dataSended,data, error);
        }

        protected void OnAfterReceiveData(DataStructToSendBase sended, byte[] data, Exception error)
        {
            DataStructToSendBase tempStruct = null;

            Interlocked.Exchange(ref tempStruct, sended);

            ThreadPool.QueueUserWorkItem((s) =>
                                             {
                                                 if (AfterReceiveData != null)
                                                 {
                                                     var lst = AfterReceiveData.GetInvocationList();

                                                     foreach (EventHandler<MasterReceiveEventArgs> del in lst)
                                                     {
                                                         try
                                                         {
                                                             del(this,new MasterReceiveEventArgs
                                                                                 {
                                                                                     Error = error,
                                                                                     SendedSData = sended,
                                                                                     ReceiveData = data,
                                                                                     ReceiveTime = DateTime.Now
                                                                                 });
                                                         }
                                                         catch (Exception)
                                                         {
                                                             continue;
                                                         }
                                                     }

                                                 }

                                                 sended.OnAfterReceiveData(this, data,error);
                                             });


        }

        public event EventHandler<MasterSendEventArgs> BeforceSendData;
        public event EventHandler<MasterSendEventArgs> AfterSendData;
        public event EventHandler<MasterReceiveEventArgs> AfterReceiveData;

        protected abstract void Dispose(bool disposing);

        public void Dispose()
        {
            if (!isDisposed)
            {
                Dispose(true);

                isDisposed = true;
            }
        }                                       

    }
}
